﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using DevExpress.Xpf.Grid;
using Microsoft.Win32;

namespace CableUnitLoadCaseGen
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            //
            GridControlNodeData.ItemsSource = NodeData;
            GridControlElemData.ItemsSource = ElemData;

            GridControlConstrainDx.ItemsSource = ConstrainDxNodes;
            GridControlConstrainDz.ItemsSource = ConstrainDzNodes;
            GridControlConstrainTension.ItemsSource = ConstrainTensionElems;
        }

        public ObservableCollection<Node> NodeData { get; } = new ObservableCollection<Node>();
        public ObservableCollection<Elem> ElemData { get; } = new ObservableCollection<Elem>();

        public ObservableCollection<ConstrainNode> ConstrainDxNodes { get; } =
            new ObservableCollection<ConstrainNode>();

        public ObservableCollection<ConstrainNode> ConstrainDzNodes { get; } =
            new ObservableCollection<ConstrainNode>();

        public ObservableCollection<ConstrainElem> ConstrainTensionElems { get; } =
            new ObservableCollection<ConstrainElem>();

        private void GridControlNodeData_OnPastingFromClipboard(object sender, PastingFromClipboardEventArgs e)
        {
            NodeData.Clear();
        }

        private void GridControlElemData_OnPastingFromClipboard(object sender, PastingFromClipboardEventArgs e)
        {
            ElemData.Clear();
        }

        private void GridControlConstrainDx_OnPastingFromClipboard(object sender, PastingFromClipboardEventArgs e)
        {
            ConstrainDxNodes.Clear();
        }

        private void GridControlConstrainDz_OnPastingFromClipboard(object sender, PastingFromClipboardEventArgs e)
        {
            ConstrainDzNodes.Clear();
        }

        private void GridControlConstrainTension_OnPastingFromClipboard(object sender, PastingFromClipboardEventArgs e)
        {
            ConstrainTensionElems.Clear();
        }

        //private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        //{
        //    GridControlNodeData.SelectAll();
        //    GridControlNodeData.CopySelectedItemsToClipboard();
        //}

        private void ButtonGenerateMCT_OnClick(object sender, RoutedEventArgs e)
        {
            if (NodeData.Count <= 0 || ElemData.Count <= 0 ||
                (ConstrainDxNodes.Count == 0 && ConstrainDzNodes.Count == 0))
            {
                MessageBox.Show("先输入节点数据和斜拉索单元数据！", "", MessageBoxButton.OK, MessageBoxImage.Warning);
                return;
            }

            var pretension = (double) SpinEditPretension.Value;
            var minDx = (double) SpinEditMinDx.Value;
            var maxDx = (double) SpinEditMaxDx.Value;
            var minDz = (double) SpinEditMinDz.Value;
            var maxDz = (double) SpinEditMaxDz.Value;
            var minTension = (double) SpinEditMinTension.Value;
            var maxTension = (double) SpinEditMaxTension.Value;

            var loadCombName = TextBoxCableUnitForceLoadCombName.Text;
            var deadLoadName1 = TextBoxDeadLoadName1.Text;
            var deadLoadName2 = TextBoxDeadLoadName2.Text;

            var allNodeDict = NodeData.ToDictionary(n => n.Id, n => n);
            var allElemList = ElemData.OrderBy(el => GetUpperLowerNode(el, allNodeDict).LowerNode.X)
                .ToList();

            //
            // group cable
            //
            Debug.Assert(allElemList.Count % 4 == 0);
            var elemGroups = new List<List<int>>();
            for (var i = 0; i < allElemList.Count / 4; i++)
            {
                var n1 = allElemList[i * 2].Id;
                var n2 = allElemList[i * 2 + 1].Id;
                var n3 = allElemList[allElemList.Count - 1 - i * 2].Id;
                var n4 = allElemList[allElemList.Count - 1 - i * 2 - 1].Id;
                elemGroups.Add(new List<int> {n1, n2, n3, n4});
            }

            //
            // output
            //
            var outputLines = new List<string>
            {
                "*STLDCASE"
            };
            outputLines.AddRange(Enumerable.Range(0, elemGroups.Count)
                .Select(i => $"T{i + 1}, USER,"));

            outputLines.AddRange(elemGroups.SelectMany((g, i) =>
            {
                var groupLines = new List<string>
                {
                    $"*USE-STLD, T{i + 1}",
                    "*PRETENSION"
                };
                groupLines.AddRange(g.Select(id => $"{id}, {pretension},"));
                return groupLines;
            }));

            outputLines.AddRange(new[]
            {
                "*LOADCOMB",
                $"NAME={loadCombName}, GEN, ACTIVE, 0, 0, , 0, 0",
                $"ST, {deadLoadName1}, 1, ST, {deadLoadName2}, 1",
                string.Join(Environment.NewLine, Enumerable.Range(0, elemGroups.Count)
                    .Select(i => $"ST, T{i + 1}, 1"))
            });

            // 
            // DX
            // 
            outputLines.AddRange(new[]
            {
                "*UNKCONS",
            });
            outputLines.AddRange(ConstrainDxNodes.Select(node =>
                $"DX_N{node.NodeId}, DISP, {node.NodeId}, 0, 0, NO, YES, {maxDx}, YES, {minDx}"));
            outputLines.AddRange(ConstrainDzNodes.Select(node =>
                $"DZ_N{node.NodeId}, DISP, {node.NodeId}, 0, 2, NO, YES, {maxDz}, YES, {minDz}"));
            outputLines.AddRange(ConstrainTensionElems.Select(elem =>
                $"T_E{elem.ElemId}, TRUSS, {elem.ElemId}, 1, 0, NO, YES, {maxTension}, YES, {minTension}"));

            outputLines.AddRange(new[]
            {
                "*UNKFACTOR",
                $"NAME=未知索力系数, {loadCombName}, SQUARE, POS, NO"
            });
            outputLines.Add(string.Join(", ",
                ConstrainDxNodes.Select(node => $"DX_N{node.NodeId}")
                    .Concat(ConstrainDzNodes.Select(node => $"DZ_N{node.NodeId}"))
                    .Concat(ConstrainTensionElems.Select(elem => $"T_E{elem.ElemId}"))));
            outputLines.Add(string.Join(", ", Enumerable.Range(0, elemGroups.Count)
                .Select(i => $"T{i + 1}, 1")));

            //
            TextBoxMct.Text = string.Join(Environment.NewLine, outputLines);
        }

        private void ButtonCopyMct_OnClick(object sender, RoutedEventArgs e)
        {
            var text = TextBoxMct.Text;
            if (string.IsNullOrWhiteSpace(text))
                return;

            Clipboard.SetText(text);
        }

        private void ButtonSaveMct_OnClick(object sender, RoutedEventArgs e)
        {
            var text = TextBoxMct.Text;
            if (string.IsNullOrWhiteSpace(text))
                return;

            var sfd = new SaveFileDialog() {FileName = "斜拉索未知系数", DefaultExt = "mct"};
            if (sfd.ShowDialog(this) != true)
                return;

            var filePath = sfd.FileName;
            File.WriteAllText(filePath, text, Encoding.GetEncoding("gb2312"));
        }

        private static (Node UpperNode, Node LowerNode) GetUpperLowerNode(Elem el,
            IReadOnlyDictionary<int, Node> nodeDict)
        {
            var n1 = nodeDict[el.N1];
            var n2 = nodeDict[el.N2];
            return n1.Z > n2.Z ? (n1, n2) : (n2, n1);
        }
    }

    public class Node
    {
        public int Id { get; set; }
        public double X { get; set; }
        public double Y { get; set; }
        public double Z { get; set; }
    }

    public class Elem
    {
        public int Id { get; set; }
        public int N1 { get; set; }
        public int N2 { get; set; }

        public string Field2 { get; set; }
        public string Field3 { get; set; }
        public string Field4 { get; set; }
        public string Field5 { get; set; }
        public string Field6 { get; set; }
    }

    public class ConstrainNode
    {
        public int NodeId { get; set; }
    }

    public class ConstrainElem
    {
        public int ElemId { get; set; }
    }
}